﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using com.IronOne.BoardPACWinAppBO.General;
using com.IronOne.BoardPACWinAppService.Xml;
using com.IronOne.BoardPACWinAppUtil.Security;
using com.IronOne.BoardPACWinAppUtil.Util;
using GalaSoft.MvvmLight.Messaging;

namespace com.IronOne.BoardPACWinAppService
{
    /// <summary>
    ///     <title>BoardPACWinApp RequestManager</title>
    ///     <description>Handle HTTP Request</description>
    ///     <copyRight>Copyright (c) 2014</copyRight>
    ///     <company>IronOne Technologies (Pvt) Ltd</company>
    ///     <createdOn>2014-11-11</createdOn>
    ///     <author>Bhathiya Dasanayake</author>
    ///     <modification>
    ///         <modifiedBy></modifiedBy>
    ///         <modifiedDate></modifiedDate>
    ///         <description></description>
    ///     </modification>
    /// </summary>
    internal static class RequestManager
    {
        public static CookieContainer CookieContainer;
        private static readonly string AppVersion;
        private static readonly string DeviceId;
        private static string _cooparateId;

        static RequestManager()
        {
            CookieContainer = new CookieContainer();
            AppVersion = DeviceService.GetAppVersion();
            _cooparateId = Global.CooperateId;
            DeviceId = DeviceService.GetAppSpecificHardwareId();
        }

        #region Messenger Handler

        private static void SendMessage(MessageDataModel mdm, string code)
        {
            var message = new NotificationMessage<MessageDataModel>(mdm, code);
            Messenger.Default.Send(message);
        }

        #endregion

        /// <summary>
        ///     Return request url
        /// </summary>
        /// <param name="postData"></param>
        /// <param name="url"></param>
        /// <param name="requestMehtod"></param>
        /// <returns></returns>
        public static async Task<ResponseDataModel> CreateRequest(Dictionary<string, string> postData, string url,
            string requestMehtod = ApplicationConstants.RequestType.POST)
        {
            return await Task.Run(() => RequestSender(postData, url, requestMehtod));
        }

        private static async Task<ResponseDataModel> RequestSender(Dictionary<string, string> postData, string url,
            string requestMehtod)
        {
            HttpWebRequest request = null;

            try
            {
                if (Global.UserForceWentOffline && !url.Equals(RequestURL.ServerUp))
                {
                    var rrdm = new ResponseDataModel
                    {
                        IsError = true,
                        ErrorMessage = "You are offline, switch to online and try again"
                    };
                    return rrdm;
                }

                var requestUrl = Global.ServerUrl + url;

                if (requestMehtod == ApplicationConstants.RequestType.POST) //Request type POST
                {
                    #region POST

                    request = (HttpWebRequest) WebRequest.Create(requestUrl);
                    request.Method = ApplicationConstants.RequestType.POST;
                    request.CookieContainer = CookieContainer;

                    //If post data available, set post data to request
                    if (postData != null && postData.Count > 0)
                    {
                        var byteArray = Encoding.UTF8.GetBytes(CreatePostData(postData));
                        using (var requestStream = await request.GetRequestStreamAsync())
                        {
                            await requestStream.WriteAsync(byteArray, 0, byteArray.Length);
                            await requestStream.FlushAsync();
                        }
                    }

                    #endregion
                }
                if (requestMehtod == ApplicationConstants.RequestType.GET) //Request type GET  
                {
                    #region GET

                    //If post data available, set post data to request
                    if (postData != null && postData.Count > 0)
                    {
                        requestUrl += CreatePostData(postData);
                    }

                    request = (HttpWebRequest) WebRequest.Create(requestUrl);
                    request.Method = ApplicationConstants.RequestType.GET;
                    request.CookieContainer = CookieContainer;

                    #endregion
                }

                // Set the ContentType property of the WebRequest.
                request.ContentType = "application/x-www-form-urlencoded";
                request.Headers["X-deviceId"] = DeviceId;
                request.Headers["X-clientTime"] = Utility.GetDateAsString(DateTime.Now);
                request.Headers["X-clientAppVersion"] = AppVersion;
                request.Headers["X-cooperateId"] = "1"; //cooparateId;

                var rdm = new ResponseDataModel();

                string responseResult;
                using (var response = (HttpWebResponse) await request.GetResponseAsync())
                {
                    using (var responseStream = response.GetResponseStream())
                    {
                        if (url.Contains("/Paper/ViewPDF?") || url.Contains("/Item/ViewItem?") ||
                            url.Contains("/Service/DownloadAnnotation?"))
                        {
                            using (var mem = new MemoryStream())
                            {
                                responseStream.CopyTo(mem);
                                rdm.Data = mem.ToArray();
                                return rdm;
                            }
                        }

                        using (var reader = new StreamReader(responseStream))
                        {
                            responseResult = reader.ReadToEnd();
                        }
                    }
                }

                rdm = XmlParser.Parse(responseResult);

                if (!rdm.IsError) return rdm;

                if (rdm.ErrorCode == ErrorCode.Err155)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.WIPEOUT_NOTIFY_TO_UI);
                }
                if (rdm.ErrorCode == ErrorCode.Err153)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.DEVICE_DEACTIVATE_NOTIFY_TO_UI);
                }
                if (rdm.ErrorCode == ErrorCode.Err110)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.SESSION_TIMEOUT_NOTIFY_TO_UI);
                }
                return rdm;
            }
            catch (WebException)
            {
                var rdm = new ResponseDataModel {IsError = true, ErrorMessage = "Unable to connect to the server"};
                return rdm;
            }
            catch (Exception)
            {
                var rdm = new ResponseDataModel {IsError = true, ErrorMessage = "General connection error"};
                return rdm;
            }
        }

        /// <summary>
        ///     Create post data as string
        /// </summary>
        /// <param name="postData"></param>
        /// <returns></returns>
        private static string CreatePostData(Dictionary<string, string> postData)
        {
            try
            {
                var postDataString = new StringBuilder();
                var itemsCount = postData.Count;

                foreach (var item in postData)
                {
                    itemsCount--;
                    var isLast = (itemsCount == 0);

                    postDataString.Append(item.Key);
                    postDataString.Append("=");
                    postDataString.Append(item.Value);
                    postDataString.Append(!isLast ? "&" : null);
                }
                return "&" + postDataString;
            }
            catch (Exception)
            {
                throw;
            }
        }

        #region CreateRequest2

        public static async Task<ResponseDataModel> CreateRequest2(Dictionary<string, string> postData, string url,
            string fileName, bool isBackup = false)
        {
            return await Task.Run(() => RequestSender2(postData, url, fileName, isBackup));
        }

        private static async Task<ResponseDataModel> RequestSender2(Dictionary<string, string> postData, string url,
            string fileName, bool isBackup = false)
        {
            try
            {
                if (Global.UserForceWentOffline && !url.Equals(RequestURL.ServerUp))
                {
                    var rrdm = new ResponseDataModel
                    {
                        IsError = true,
                        ErrorMessage = "You are offline, switch to online and try again"
                    };
                    return rrdm;
                }

                var requestUrl = Global.ServerUrl + url;

                #region Create Multipart

                if (postData != null && postData.Count > 0)
                {
                    requestUrl += CreatePostData(postData);
                }

                var request = (HttpWebRequest) WebRequest.Create(requestUrl);
                request.Method = ApplicationConstants.RequestType.POST;
                request.CookieContainer = CookieContainer;

                //If post data available, set post data to request
                if (postData != null)
                {
                    // Create a boundry
                    var boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
                    request.ContentType = "multipart/form-data; boundary=" + boundary;

                    // Get the boundry in bytes
                    var boundarybytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
                    // Get the header for the file upload
                    const string headerTemplate = "Content-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n";
                    // Add the filename to the header
                    var header = string.Format(headerTemplate, "file", "filesList");
                    //convert the header to a byte array
                    var headerbytes = Encoding.UTF8.GetBytes(header);

                    StorageFolder folder;
                    if (isBackup)
                        folder = await Utility.GetShareOriginalFolder();
                    else
                        folder = await Utility.GetShareSentFolder();

                    var anntotationFile = await folder.GetFileAsync(fileName);
                    var file = await GetBytesAsync(anntotationFile);

                    using (var requestStream = await request.GetRequestStreamAsync())
                    {
                        // Write out the starting boundry
                        await requestStream.WriteAsync(boundarybytes, 0, boundarybytes.Length);
                        // Write the header including the filename.
                        await requestStream.WriteAsync(headerbytes, 0, headerbytes.Length);
                        await requestStream.WriteAsync(file, 0, file.Length);

                        boundarybytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");

                        // Write out the trailing boundry
                        await requestStream.WriteAsync(boundarybytes, 0, boundarybytes.Length);
                        await requestStream.FlushAsync();
                    }
                }

                #endregion

                request.Headers["X-deviceId"] = DeviceId;
                request.Headers["X-clientTime"] = Utility.GetDateAsString(DateTime.Now);
                request.Headers["X-clientAppVersion"] = AppVersion;
                request.Headers["X-cooperateId"] = "1"; //cooparateId;

                string responseResult;
                using (var response = (HttpWebResponse) await request.GetResponseAsync())
                {
                    using (var responseStream = response.GetResponseStream())
                    {
                        using (var reader = new StreamReader(responseStream))
                        {
                            responseResult = reader.ReadToEnd();
                        }
                    }
                }

                var rdm = XmlParser.Parse(responseResult);

                if (!rdm.IsError) return rdm;

                if (rdm.ErrorCode == ErrorCode.Err155)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.WIPEOUT_NOTIFY_TO_UI);
                }
                if (rdm.ErrorCode == ErrorCode.Err153)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.DEVICE_DEACTIVATE_NOTIFY_TO_UI);
                }
                if (rdm.ErrorCode == ErrorCode.Err110)
                {
                    SendMessage(new MessageDataModel {MessageData = rdm.ErrorMessage},
                        MessageCodes.SESSION_TIMEOUT_NOTIFY_TO_UI);
                }
                return rdm;
            }
            catch (WebException)
            {
                var rdm = new ResponseDataModel {IsError = true, ErrorMessage = "Unable to connect to the server"};
                return rdm;
            }
            catch (Exception)
            {
                var rdm = new ResponseDataModel {IsError = true, ErrorMessage = "General connection error"};
                return rdm;
            }
        }
        
        /// <summary>
        ///     Returns byte array from StorageFile. Author : Farhan Ghumra
        /// </summary>
        private static async Task<byte[]> GetBytesAsync(StorageFile file)
        {
            byte[] fileBytes;
            using (var stream = await file.OpenReadAsync())
            {
                fileBytes = new byte[stream.Size];
                using (var reader = new DataReader(stream))
                {
                    await reader.LoadAsync((uint) stream.Size);
                    reader.ReadBytes(fileBytes);
                }
            }

            return fileBytes;
        }

        #endregion
    }
}